package com.ifonly.mvc;

import com.ifonly.interceptor.*;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * ActionMapper 用于配置 Action 请求信息
 *
 * @author <a href="mailto:ifonlymaster@163.com">ifonly</a>
 * @version 1.0 2015-12-09 14:46
 * @since JDK 1.6
 */
public class ActionMapper {

    private GlobalInterceptors globalInterceptors = GlobalInterceptors.getInstance();

    private Map<String, Action> actionMapping = new HashMap<String, Action>();

    private ActionMapper() {

    }

    private static class InstanceHolder {
        private static ActionMapper instance = new ActionMapper();
    }

    public static ActionMapper getInstance() {
        return InstanceHolder.instance;
    }

    @SuppressWarnings("unchecked")
    public void addAction(String actionUrl, String controllerClassName, String methodName) {
        addAction(actionUrl, extractViewDir(actionUrl), controllerClassName, methodName);
    }

    private static String extractViewDir(String actionUrl) {
        if (actionUrl.lastIndexOf("/") == 0) {
            return "/";
        }
        return actionUrl.substring(0, actionUrl.lastIndexOf("/"));
    }

    public void addAction(String actionUrl, String viewDir, String controllerClassName, String methodName) {
        try {
            Class<?> clazz = Class.forName(controllerClassName);
            if (Controller.class.isAssignableFrom(clazz)) {
                addAction(actionUrl, viewDir, controllerClassName, (Class<? extends Controller>) clazz, methodName);
            } else {
                throw new IllegalArgumentException("Class[" + controllerClassName + "] not the subclass of com.ifonly.mvc.Controller");
            }
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Class[" + controllerClassName + "] not found", e);
        }
    }

    public void addAction(String actionUrl, String viewDir, String controllerClassName, Class<? extends Controller> controllerClass, String methodName) {
        try {
            Method method = controllerClass.getMethod(methodName);
            addAction(actionUrl, viewDir, controllerClassName, controllerClass, methodName, method);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("No method[" + methodName + "] in Class[" + controllerClass.getName() + "]", e);
        }
    }

    public void addAction(String actionUrl, String viewDir, String controllerClassName,
                          Class<? extends Controller> controllerClass,
                          String methodName, Method method) {
        Interceptor[] globals = globalInterceptors.globalInterceptors();
        Interceptor[] classes = getInterceptors(controllerClass.getAnnotation(Intercepts.class));
        Interceptor[] methods = getInterceptors(method.getAnnotation(Intercepts.class));

        Ignore ignore = method.getAnnotation(Ignore.class);
        Class<? extends Interceptor>[] ignores = null;
        if (ignore != null) {
            ignores = ignore.value();
        }

        Interceptor[] interceptors = InterceptorBuilder.build(globals, classes, methods, ignores);

        addAction(actionUrl, viewDir, controllerClassName, controllerClass, methodName, method, interceptors);

    }

    public void addAction(String actionUrl, String viewDir,
                          String controllerClassName,
                          Class<? extends Controller> controllerClass,
                          String methodName, Method method, Interceptor[] interceptors) {
        Action action = new Action(actionUrl, viewDir, controllerClassName,
                controllerClass, methodName, method, interceptors);

        addAction(actionUrl, action);
    }

    public void addAction(String actionUrl, Action action) {
        actionMapping.put(actionUrl, action);
    }

    public Action getAction(String actionUrl) {
        return actionMapping.get(actionUrl);
    }

    private Interceptor[] getInterceptors(Intercepts interceptsAnnotation) {
        if (interceptsAnnotation == null) {
            return new Interceptor[0];
        }

        Class<? extends Interceptor>[] classes = interceptsAnnotation.value();
        int len = classes.length;
        Interceptor[] results = new Interceptor[len];

        try {
            for (int i = 0; i < len; i++) {
                Interceptor interceptor = classes[i].newInstance();
                results[i] = interceptor;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return results;
    }


    public Set<String> allMappingUrl() {
        return actionMapping.keySet();
    }

    Collection<Action> allMappingAction() {
        return actionMapping.values();
    }

}
